import Docxtemplater from 'docxtemplater'
// @ts-ignore
import ImageModule from 'docxtemplater-image-module-free'
import PizZip from 'pizzip'
import PizZipUtils from 'pizzip/utils'
// @ts-ignore
import saveAs from 'pizzip/vendor/FileSaver'
/**
 * 将base64编码的字符串转换为文件对象
 *
 * @param data base64编码的字符串
 * @param filename 文件名，默认为 'base64ToFile'
 * @returns 返回生成的 File 对象
 */
export function base64ToFile(data: string, filename = 'base64ToFile') {
  // 将base64转换为文件
  const arr = data.split(',')
  const mime = arr?.[0]?.match?.(/:(.*?);/)?.[1]
  const bstr = atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) u8arr[n] = bstr.charCodeAt(n)
  return new File([u8arr], filename, { type: mime })
}

/**
 * 将文件转换为Base64字符串
 * @param file - 前端上传的文件对象
 * @returns Promise<string> - 返回包含Base64字符串的Promise
 */
export function fileToBase64(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()

    // 监听读取完成事件
    reader.onload = () => {
      // 检查结果是否存在
      if (reader.result && typeof reader.result === 'string') {
        resolve(reader.result)
      }
      else {
        reject(new Error('文件读取失败：结果无效'))
      }
    }

    // 监听错误事件
    reader.onerror = (error) => {
      reject(new Error(`文件读取失败: ${error}`))
    }
    // 读取文件内容（自动转换为Base64）
    reader.readAsDataURL(file)
  })
}

/**
 * 将 base64 编码的 DataURL 转换为 ArrayBuffer
 * @param dataURL base64 编码的 DataURL 字符串，格式为 "data:image/xxx;base64,..."
 * @returns 转换后的 ArrayBuffer，如果输入的 dataURL 格式不正确，则返回 false
 */
export function base64ToArrayBuffer(dataURL: string) {
  const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/
  if (!base64Regex.test(dataURL)) {
    return false
  }
  const stringBase64 = dataURL.replace(base64Regex, '')
  let binaryString = ''
  if (typeof window !== 'undefined') {
    // For browsers, return a string (of binary content) :
    binaryString = window.atob(stringBase64)
  }
  const len = binaryString.length
  const bytes = new Uint8Array(len)
  for (let i = 0; i < len; i++) {
    const ascii = binaryString.charCodeAt(i)
    bytes[i] = ascii
  }
  return bytes.buffer
}

/**
 * 将 ArrayBuffer 转换为 Base64 字符串
 * @param buffer - 要转换的 ArrayBuffer
 * @param mimeType - 可选的 MIME 类型（如 'image/png'），默认不添加 Data URL 前缀
 * @returns 如果提供 mimeType，返回完整的 Data URL（如 ''）；
 *          否则返回纯 Base64 字符串（如 'xxxx'）
 */
export function arrayBufferToBase64(
  buffer: ArrayBuffer,
  mimeType?: string,
): string {
  let binaryString = ''
  const bytes = new Uint8Array(buffer)
  const len = bytes.byteLength

  // 将 ArrayBuffer 转换为二进制字符串
  for (let i = 0; i < len; i++) {
    binaryString += String.fromCharCode(bytes[i])
  }

  // 转换为 Base64
  const base64 = window.btoa(binaryString)

  // 如果提供了 MIME 类型，返回 Data URL；否则返回纯 Base64
  if (mimeType) {
    return `data:${mimeType};base64,${base64}`
  }
  return base64
}

/**
 * 文件下载
 * @param file - File
 */
export function downloadFile(file: File) {
  const link = document.createElement('a') // 创建一个a标签
  link.download = file.name
  const href = URL.createObjectURL(file) // 获取url
  link.href = href
  document.body.appendChild(link)
  link.click()
  URL.revokeObjectURL(href) // 释放url
  document.body.removeChild(link)
}

/**
 * 文件下载
 * @param fileName - 文件名加后缀
 * @param href - 文件下载链接
 */
export function downloadFileHref(fileName: string, href: string) {
  const link = document.createElement('a') // 创建一个a标签
  link.download = fileName
  link.href = href
  document.body.appendChild(link)
  link.click()
  URL.revokeObjectURL(href) // 释放url
  document.body.removeChild(link)
}

/**
 * 下载文件流函数
 *
 * @param res 请求响应对象
 * @param options 配置对象
 * @param options.fileName 文件名（可选），默认为后端返回的文件名
 * @param options.ext 文件扩展名（可选），默认为后端返回的扩展名，如：.txt、.pdf等
 * @param options.callback 回调函数（可选）
 */
export function downLoadFileStream(
  res: any,
  options: {
    fileName?: string
    ext?: string
    callback?: () => void
  },
) {
  const contentType = res.headers['content-type'] ?? res.headers['Content-Type']
  //  返回异常信息时请求头含'json'字样

  if (contentType?.includes('json')) {
    const reader = new FileReader()
    reader.readAsText(res.data, 'utf-8')
    // 读取异常信息
    reader.onload = () => {
      if (reader) {
        const resStr = reader?.result as string
        const jsonObj = JSON.parse(resStr)
        throw new Error(jsonObj.msg)
      }
    }
  }
  else {
    // 正常返回文件流是size通常大于0
    if (res.data.size) {
      // 读取后台返回的文件名
      const requestFileName
        = res.headers['content-disposition'] ?? res.headers['Content-Disposition']
      let fileName = ''
      let extension = ''
      if (options?.ext && options?.ext.indexOf('.') !== 0) {
        options.ext = `.${options?.ext}`
      }
      if (requestFileName) {
        fileName
          = options?.fileName
          ?? decodeURIComponent(
            requestFileName.slice(
              requestFileName.indexOf('=') + 1,
              requestFileName.lastIndexOf('.'),
            ),
          )
        // 读取后台返回的文件扩展名
        extension
          = options?.ext
          ?? requestFileName.slice(
            requestFileName.lastIndexOf('.'),
            requestFileName.length,
          )
      }
      else {
        fileName = options?.fileName ?? Date.now().toString()
        extension = options?.ext ?? '.error'
      }
      const r = new FileReader()
      // 通过实现a标签点击下载文件
      r.onload = function () {
        fileName = fileName + extension
        const link = document.createElement('a')
        const blob = new Blob([res.data])
        link.style.display = 'none'
        const href = URL.createObjectURL(blob)
        link.href = href
        link.download = fileName
        document.body.appendChild(link)
        link.click()
        URL.revokeObjectURL(href) // 释放url
        document.body.removeChild(link)
        options?.callback?.()
      }
      r.readAsText(res.data)
    }
    else {
      throw new Error('Error, 后台无文件流返回')
    }
  }
}

/**
 * 根据json的地址下载json
 * @param url - json地址
 * @param name - 下载的文件名
 */
export function downloadJsonFile(url: string, name?: string) {
  return new Promise<void>((resolve, reject) => {
    // 创建一个新的 XMLHttpRequest 对象
    const xhr = new XMLHttpRequest()

    // 设置请求的数据类型为 JSON
    xhr.responseType = 'json'

    // 发送 HTTP 请求
    xhr.open('GET', url)
    xhr.send()

    // 当请求完成时，执行以下操作
    xhr.onload = function () {
      // 将 JSON 数据转换为字符串
      const jsonString = JSON.stringify(xhr.response)

      // 创建一个新的 Blob 对象
      const blob = new Blob([jsonString], { type: 'application/json' })

      // 创建一个新的链接
      const link = document.createElement('a')
      link.href = URL.createObjectURL(blob)
      // 设置链接的下载属性
      link.download = name ?? 'data' + '.json'
      // 将链接添加到页面中并点击它
      document.body.appendChild(link)
      link.click()
      // 释放链接所占用的内存
      URL.revokeObjectURL(link.href)
      document.body.removeChild(link)
      resolve()
    }
    xhr.onerror = function () {
      reject(new Error('下载失败'))
    }
  })
}

/**
 * 传入string类型数据并下载到客户端，支持json文件、文本文件和html文件
 * @param data - 数据
 * @param fileName - 下载的文件名，可以根据mimeType自动赋值扩展名
 * @param mimeType - 'application/json' | 'text/plain' | 'text/html' 分别对应 json文件、文本文件和html文件
 */
export function downloadTextFile(
  data: string,
  fileName: string,
  mimeType: 'application/json' | 'text/plain' | 'text/html' = 'text/plain',
) {
  let blob: Blob | undefined
  if (typeof data === 'string') {
    // 如果传入的是字符串，我们需要创建一个Blob对象
    blob = new Blob([data], { type: mimeType })
    if (!fileName.includes('.')) {
      // 根据MIME类型添加合适的文件扩展名（这里是一个简单的示例，你可能需要更复杂的逻辑来确定扩展名）
      const extensionMap: Record<string, string> = {
        'text/plain': '.txt',
        'text/html': '.html',
        'application/json': '.json',
        // ... 其他MIME类型到扩展名的映射
      }
      const extension = extensionMap[mimeType] || '.error' // 默认使用.error作为未知类型的扩展名
      fileName += extension
    }
    // 创建一个指向Blob对象的URL
    const url = URL.createObjectURL(blob)
    // 创建一个隐藏的<a>元素并设置其属性
    const a = document.createElement('a')
    a.href = url
    a.download = fileName
    // 将<a>元素添加到文档中（虽然它是隐藏的，但这一步是必要的）
    document.body.appendChild(a)
    // 模拟点击<a>元素以触发下载
    a.click()
    // 释放URL对象（不再需要时）
    URL.revokeObjectURL(url)
    // 从文档中移除<a>元素（可选，但推荐这样做以清理DOM）
    document.body.removeChild(a)
  }
}

/**
 * 获取文件名的后缀
 * @param fileName - 文件名
 * @returns suffix:'.png'
 */
export function getSuffix(fileName: string): string {
  const index = fileName.lastIndexOf('.')
  return fileName.slice(index)
}

/**
 * 从url中获取文件名 + 后缀 如：http://asdasd/asdsad/asdsad/阿萨德.txt  =>  阿萨德.txt
 * @param val
 */
export function getFileName(val: string): string {
  return val.slice(Math.max(0, val.lastIndexOf('/') + 1))
}

/**
 * 生成一个带有图片的 Word 文档
 * @param options 配置选项
 * @param options.template Word 模板的 URL地址, 如果模版放在'public/example' 下 则 地址可以输入：'examples/image-example.docx'
 * @param options.data 模版替换数据，数据里的图片支持静态资源路径或base64字符串
 */
export function genDocx(options: {
  template: string
  data: Record<string, any>
}) {
  const imageOptions = {
    centered: true,
    getImage(tagValue: any) {
      const res: any = base64ToArrayBuffer(tagValue)
      if (res) {
        return res
      }
      else {
        // In this case tagValue will be a URL tagValue = "https://docxtemplater.com/puffin.png"
        return new Promise((resolve, reject) => {
          PizZipUtils.getBinaryContent(tagValue, (error, content) => {
            if (error) {
              return reject(error)
            }
            return resolve(content)
          })
        })
      }
    },
    getSize(img: any, tagValue: any, tagName: any) {
      return new Promise((resolve, reject) => {
        const image = new Image()
        image.src = tagValue
        image.onload = function () {
          resolve([image.width, image.height])
        }
        image.onerror = function (e) {
          console.log('img, tagValue, tagName : ', img, tagValue, tagName)
          reject(e)
        }
      })
    },
  }
  const docxType
    = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  PizZipUtils.getBinaryContent(options.template, (error, content) => {
    if (error) {
      console.error(error)
      return
    }

    const zip = new PizZip(content)
    const doc = new Docxtemplater(zip, {
      modules: [new ImageModule(imageOptions)],
    })

    doc.renderAsync(options.data).then(() => {
      const out = doc.getZip().generate({
        type: 'blob',
        mimeType: docxType,
      })
      saveAs(out, 'generated.docx')
    })
  })
}
